package com.example.StrJson;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.*;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;


/**
 * @author zqr
 */
public class GeoJsonUtils {
    private final GeometryFactory geometryFactory;

    private CoordinateReferenceSystem targetCRS;

    private CoordinateReferenceSystem sourceCRS;

    public GeoJsonUtils() {

        this.geometryFactory = JTSFactoryFinder.getGeometryFactory();
        try {
            this.targetCRS = CRS.decode("EPSG:4326");
            this.sourceCRS = CRS.decode("EPSG:4326");

        } catch (FactoryException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据类型将GeoJson转换成Geometry实体
     * 目前只有
     *
     * @param type         数据类型
     * @param geometryJson geoJson对象
     * @return Geometry
     */
    public Geometry createGeometry(String type, JSONObject geometryJson) {
        switch (type) {
            case "Polygon":
                return createPolygon(geometryJson);
            case "LineString":
                return createLineString(geometryJson);
            case "Point":
                return createPoint(geometryJson);
            case "MultiPoint":
                return createMultiPoint(geometryJson);
            case "MultiLineString":
                return createMultiLineString(geometryJson);
            case "MultiPolygon":
                return createMultiPolygon(geometryJson);
            default:
                break;
        }
        return null;
    }

    /**
     * 根据json对象创建点（Point）实例
     *
     * @param geometryJson geoJson对象
     * @return Point
     */
    public Point createPoint(JSONObject geometryJson) {
        JSONArray coordinatesJson = geometryJson.getJSONArray("coordinates");
        double lon = coordinatesJson.getDouble(1);
        double lat = coordinatesJson.getDouble(0);
        Coordinate coordinate = new Coordinate();
        try {
            MathTransform mathTransform = CRS.findMathTransform(sourceCRS, targetCRS, true);
            JTS.transform(new Coordinate(lon, lat), coordinate, mathTransform);
        } catch (FactoryException | TransformException e) {
            e.printStackTrace();
        }
        return geometryFactory.createPoint(new Coordinate(coordinate.y, coordinate.x));
    }

    /**
     * 根据json对象创建线（LineString）实例
     *
     * @param geometryJson geoJson对象
     * @return LineString
     */
    public LineString createLineString(JSONObject geometryJson) {
        JSONArray coordinatesJson = geometryJson.getJSONArray("coordinates");
        int length = coordinatesJson.size();
        Coordinate[] coordinates = new Coordinate[length];
        for (int i = 0; i < length; i++) {
            JSONArray coord = coordinatesJson.getJSONArray(i);
            double lon = coord.getDouble(1);
            double lat = coord.getDouble(0);
            Coordinate coordinate = new Coordinate();
            try {
                MathTransform mathTransform = CRS.findMathTransform(sourceCRS, targetCRS, true);
                JTS.transform(new Coordinate(lon, lat), coordinate, mathTransform);
            } catch (TransformException | FactoryException e) {
                e.printStackTrace();
            }
            coordinates[i] = new Coordinate(coordinate.y, coordinate.x);
        }
        return geometryFactory.createLineString(coordinates);
    }

    /**
     * 根据json对象创建面（Polygon）实例
     *
     * @param geometryJson geoJson对象
     * @return Polygon
     */
    public Polygon createPolygon(JSONObject geometryJson) {
        JSONArray coordinatesJson = geometryJson.getJSONArray("coordinates").getJSONArray(0);
        int length = coordinatesJson.size();
        Coordinate[] coordinates = new Coordinate[length];
        for (int i = 0; i < length; i++) {
            JSONArray coord = coordinatesJson.getJSONArray(i);
            double lon = coord.getDouble(1);
            double lat = coord.getDouble(0);
            Coordinate coordinate = new Coordinate();
            try {
                MathTransform mathTransform = CRS.findMathTransform(sourceCRS, targetCRS, true);
                JTS.transform(new Coordinate(lon, lat), coordinate, mathTransform);
            } catch (TransformException | FactoryException e) {
                e.printStackTrace();
            }
            coordinates[i] = new Coordinate(coordinate.y, coordinate.x);
        }
        //线性环
        LinearRing ring = geometryFactory.createLinearRing(coordinates);
        //面的内部孔
        LinearRing[] holes = null;
        return geometryFactory.createPolygon(ring, holes);
    }

    /**
     * 根据json对象创建点（MultiPoint）实例
     *
     * @param geometryJson geoJson对象
     * @return MultiPoint
     */
    public MultiPoint createMultiPoint(JSONObject geometryJson) {
        JSONArray coordinatesJson = geometryJson.getJSONArray("coordinates");
        Point[] points;
        List<Point> pointList = new ArrayList<>();
        for (int i = 0; i < coordinatesJson.size(); i++) {
            JSONArray json = (JSONArray) coordinatesJson.get(i);
            double lon = json.getDouble(1);
            double lat = json.getDouble(0);
            Coordinate coordinate = new Coordinate();
            try {
                MathTransform mathTransform = CRS.findMathTransform(sourceCRS, targetCRS, true);
                JTS.transform(new Coordinate(lon, lat), coordinate, mathTransform);
            } catch (FactoryException | TransformException e) {
                e.printStackTrace();
            }
            Point point = geometryFactory.createPoint(new Coordinate(coordinate.y, coordinate.x));
            pointList.add(point);
        }
        points = pointList.toArray(new Point[0]);
        return geometryFactory.createMultiPoint(points);
    }

    /**
     * 根据json对象创建线（MultiLineString）实例
     *
     * @param geometryJson geoJson对象
     * @return MultiLineString
     */
    public MultiLineString createMultiLineString(JSONObject geometryJson) {
        JSONArray coordinatesJson = geometryJson.getJSONArray("coordinates");
        LineString[] lineStrings;
        List<LineString> lineStringList = new ArrayList<>();
        coordinatesJson.forEach(json->{
            JSONArray jsonArray = (JSONArray) json;
            int length = jsonArray.size();
            Coordinate[] coordinates = new Coordinate[length];
            for (int i = 0; i < length; i++) {
                JSONArray coord = jsonArray.getJSONArray(i);
                double lon = coord.getDouble(1);
                double lat = coord.getDouble(0);
                Coordinate coordinate = new Coordinate();
                try {
                    MathTransform mathTransform = CRS.findMathTransform(sourceCRS, targetCRS, true);
                    JTS.transform(new Coordinate(lon, lat), coordinate, mathTransform);
                } catch (TransformException | FactoryException e) {
                    e.printStackTrace();
                }
                coordinates[i] = new Coordinate(coordinate.y, coordinate.x);
            }
            LineString lineString = geometryFactory.createLineString(coordinates);
            lineStringList.add(lineString);
        });
        lineStrings = lineStringList.toArray(new LineString[0]);
        return geometryFactory.createMultiLineString(lineStrings);
    }

    /**
     * 根据json对象创建面（MultiPolygon）实例
     *
     * @param geometryJson geoJson对象
     * @return MultiPolygon
     */
    public MultiPolygon createMultiPolygon(JSONObject geometryJson) {
        JSONArray multiPolygon = geometryJson.getJSONArray("coordinates");
        Polygon[] polygons;
        List<Polygon> polygonList = new ArrayList<>();
        multiPolygon.forEach(json->{
            JSONArray jsonArray = (JSONArray) json;
            jsonArray.forEach(childJson->{
                JSONArray coordinatesJson = (JSONArray) childJson;
                int length = coordinatesJson.size();
                Coordinate[] coordinates = new Coordinate[length];
                for (int i = 0; i < length; i++) {
                    JSONArray coord = coordinatesJson.getJSONArray(i);
                    double lon = coord.getDouble(1);
                    double lat = coord.getDouble(0);
                    Coordinate coordinate = new Coordinate();
                    try {
                        MathTransform mathTransform = CRS.findMathTransform(sourceCRS, targetCRS, true);
                        JTS.transform(new Coordinate(lon, lat), coordinate, mathTransform);
                    } catch (TransformException | FactoryException e) {
                        e.printStackTrace();
                    }
                    coordinates[i] = new Coordinate(coordinate.y, coordinate.x);
                }
                //线性环
                LinearRing ring = geometryFactory.createLinearRing(coordinates);
                //面的内部孔
                LinearRing[] holes = null;
                Polygon polygon = geometryFactory.createPolygon(ring, holes);
                polygonList.add(polygon);
            });
        });

        polygons = polygonList.toArray(new Polygon[0]);
        return geometryFactory.createMultiPolygon(polygons);
    }

    /**
     * geoJson对象转shapefile文件
     *
     * @param jsonObject    geoJson对象
     * @param shapefilePath 矢量文件地址
     */
    public void geoJsonToShapeFile(JSONObject jsonObject, String shapefilePath) {
        JSONArray features = jsonObject.getJSONArray("features");
        JSONObject feature0 = features.getJSONObject(0);
        Set<String> properties = feature0.getJSONObject("properties").keySet();
        String strType = feature0.getJSONObject("geometry").getString("type");
        File file = new File(shapefilePath);
        Map<String, Serializable> params = new HashMap<>();
        try {
            Class<?> geoType = null;
            switch (strType) {
                case "Point":
                    geoType = Point.class;
                    break;
                case "MultiPoint":
                    geoType = MultiPoint.class;
                    break;
                case "LineString":
                    geoType = LineString.class;
                    break;
                case "MultiLineString":
                    geoType = MultiLineString.class;
                    break;
                case "Polygon":
                    geoType = Polygon.class;
                    break;
                case "MultiPolygon":
                    geoType = MultiPolygon.class;
                    break;
                default:
                    break;
            }
            params.put(ShapefileDataStoreFactory.URLP.key, file.toURI().toURL());
            ShapefileDataStore ds = (ShapefileDataStore) new ShapefileDataStoreFactory().createNewDataStore(params);
//            ds.setCharset(Charset.forName("GBK"));
            //ds.setCharset(Charset.forName("GB2312"));
            ds.setCharset(StandardCharsets.UTF_8);
            SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
            tb.setCRS(DefaultGeographicCRS.WGS84);
            tb.setName("shapefile");
            tb.add("the_geom", geoType);
            //编号
            tb.add("id", Integer.class);
            // 设置属性
            for (Object property : properties) {
                String str = property.toString();
                // 这里注意一下，设置属性的标题不能超过10个字符（比如：SHAPE_Area可以）
                tb.add(str, String.class);
            }
            ds.createSchema(tb.buildFeatureType());

            FeatureWriter<SimpleFeatureType, SimpleFeature> writer = ds.getFeatureWriter(ds.getTypeNames()[0], Transaction.AUTO_COMMIT);
            SimpleFeature feature;
            for (int i = 0; i < features.size(); i++) {
                feature = writer.next();
                JSONObject object = features.getJSONObject(i);
                JSONObject geometryJson = object.getJSONObject("geometry");
                JSONObject propertiesJson = object.getJSONObject("properties");
                Geometry geometry = createGeometry(strType, geometryJson);
                feature.setAttribute("the_geom", geometry);
                feature.setAttribute("id", i + 1);
                for (Object property : properties) {
                    String str = property.toString();
                    String s = propertiesJson.getString(str);
                    feature.setAttribute(str, s);
                }
            }
            writer.write();
            writer.close();
            ds.dispose();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    public static String readFile(String path) {
        StringBuilder laststr = new StringBuilder();
        File file = new File(path);
        if (!file.exists()) {
            return null;
        }
        BufferedReader reader = null;
        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
            reader = new BufferedReader(inputStreamReader);
            String tempString;
            while ((tempString = reader.readLine()) != null) {
                laststr.append(tempString);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return laststr.toString();
    }

    /**
     * 工具类测试方法
     * @param args
     */
    public static void main(String[] args){
        long start = System.currentTimeMillis();
        //String shpPath =System.getProperty("user.dir")+File.separator+"testShp1"+File.separator+"qwe.shp";
        String shpPath ="D:\\项目数据\\data\\梁溪数据\\北大街街道\\凤凰城\\qwe.shp";
//        String jsonPath = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{\"name\":1},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[120.35595417022705,31.598129835163153],[120.35604000091553,31.597800858585263],[120.35634040832518,31.597106348660574],[120.35689830780028,31.59571731327566],[120.35685539245604,31.594657246019707],[120.35629749298094,31.593085399952464],[120.36179065704346,31.59147697187113],[120.3638505935669,31.59611940459624],[120.35595417022705,31.598129835163153]]]}},{\"type\":\"Feature\",\"properties\":{\"name\":2},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[120.36445140838623,31.59597318977138],[120.36252021789551,31.59114797179716],[120.36642551422119,31.589941628252642],[120.3678846359253,31.595022787815537],[120.36445140838623,31.59597318977138]]]}},{\"type\":\"Feature\",\"properties\":{\"name\":3},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[120.356125831604,31.592811238038273],[120.35546064376832,31.59156836057438],[120.35481691360475,31.589777125649977],[120.36073923110963,31.588643877606124],[120.3618335723877,31.59102002701019],[120.356125831604,31.592811238038273]]]}},{\"type\":\"Feature\",\"properties\":{\"name\":4},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[120.36226272583006,31.590928637769075],[120.36123275756836,31.588515929380392],[120.36556720733641,31.587675122383125],[120.36627531051636,31.58968573518969],[120.36226272583006,31.590928637769075]]]}}]}";
//        String jsonPath = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"coordinates\":[[106.54560955114829,29.554035336177876],[106.53817181648725,29.550980003829153]],\"type\":\"MultiPoint\"}}]}";
//        String jsonPath = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{\"name\":1},\"geometry\":{\"coordinates\":[[[106.52689073981105,29.545880927906055],[106.52857737836814,29.546370030032236],[106.53014354274296,29.546998872146375],[106.53223176190835,29.547732516334506],[106.53624756799695,29.554614538226033]],[[106.51978276498585,29.54479790808631],[106.51946150049866,29.549304593545685],[106.52275446149031,29.54553156824933]]],\"type\":\"MultiLineString\"}}]}";
//        String jsonPath = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{\"name\":135},\"geometry\":{\"coordinates\":[[[[106.51968253122806,29.54939731918404],[106.51968253122806,29.545340993644174],[106.52363454713759,29.545340993644174],[106.52363454713759,29.54939731918404],[106.51968253122806,29.54939731918404]],[[106.5198136545211,29.54109353498211],[106.51553100472353,29.54109353498211],[106.51553100472353,29.538033523331848],[106.5198136545211,29.538033523331848],[106.5198136545211,29.54109353498211]]]],\"type\":\"MultiPolygon\"}}]}";

        String jsonPath = readFile("D:\\项目数据\\data\\梁溪数据\\北大街街道\\凤凰城\\222.json");
        JSONObject json = JSONObject.parseObject(jsonPath);
        GeoJsonUtils geoUtils = new GeoJsonUtils();
        geoUtils.geoJsonToShapeFile(json, shpPath);
        System.out.println("共耗时" + (System.currentTimeMillis() - start) + "ms");

    }

}
